using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Microsoft.Data.Schema.Extensibility;
using Microsoft.Data.Schema.SchemaModel;
using Microsoft.Data.Schema.ScriptDom.Sql;
using Microsoft.Data.Schema.Sql.SchemaModel;
using Microsoft.Data.Schema.Sql;
using Microsoft.Data.Schema.StaticCodeAnalysis;
using System.IO;
using System.Text.RegularExpressions;
using System.Linq;

namespace Neznayka
{
    /// <summary>
    /// This is a SQL rule which returns a warning message 
    /// whenever it finds a parameter name that is too short.
    /// </summary>

    [DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))]
    [DataRuleAttribute(
        NeznaykaConstants.NameSpace,
        NeznaykaConstants.ParameterVariableNameFormatRuleId,
        NeznaykaConstants.ResourceBaseName,
        NeznaykaConstants.ParameterVariableNameFormat_RuleName,
        NeznaykaConstants.CategoryVariableNaming,
        DescriptionResourceId = NeznaykaConstants.ParameterVariableNameFormat_ProblemDescription)]
    [SupportedElementType(typeof(ISqlFunction))]
    [SupportedElementType(typeof(ISqlTableValuedFunction))]
    [SupportedElementType(typeof(ISqlMultiStatementTableValuedFunction))]
    [SupportedElementType(typeof(ISqlProcedure))]
    public class ParameterVariableNameFormatRule : StaticCodeAnalysisRule
    {
        #region Overrides
        /// <summary>
        /// Analyze the model element
        /// </summary>
        public override IList<DataRuleProblem> Analyze(DataRuleSetting ruleSetting, DataRuleExecutionContext context)
        {

            // (Re)-Load Environment settings
            List<DataRuleProblem> problems;
            SqlSchemaModel sqlSchemaModel;
            ISqlModelElement sqlElement;
            TSqlFragment sqlFragment;
            DMVRuleSetup.RuleSetup(context, out problems, out sqlSchemaModel, out sqlElement, out sqlFragment);

            // visitor to get the declarations of variables
            VariableDefinitionVisitor variableDefinitionVisitor = new VariableDefinitionVisitor();
            sqlFragment.Accept(variableDefinitionVisitor);
            List<DeclareVariableElement> variableDeclarations = variableDefinitionVisitor.VariableDeclarations;
            // visitor to get the declarations of parameters
            ParameterDeclarationVisitor parameterDeclarationVisitor = new ParameterDeclarationVisitor();
            sqlFragment.Accept(parameterDeclarationVisitor);
            IEnumerable<ProcedureParameter> parameterDeclarations = parameterDeclarationVisitor.ParameterDeclarations;

            List<Literal> allVarNames = variableDeclarations.Select(n => n.VariableName).ToList();
            List<Identifier> allParamNames = parameterDeclarations.Select(n => n.Name).ToList();

            foreach (var varName in allVarNames)
            {
                // the var name includes the leading @ - strip it out here.
                if (!DMVSettings.ValidParameterAndVariableRegexpAsRegExp.IsMatch(varName.Value.Substring(1)))
                {
                    DataRuleProblem problem = new DataRuleProblem(this,
                                                String.Format(CultureInfo.CurrentCulture, this.RuleProperties.Description, SqlRuleUtils.GetElementName(sqlSchemaModel, sqlElement)),
                                                sqlElement);

                    SqlRuleUtils.UpdateProblemPosition(problem, varName.StartOffset, varName.FragmentLength);
                    problems.Add(problem);
                }
            }
            foreach (var paramName in allParamNames)
            {
                // the param name includes the leading @ - strip it out here.
                if ( ! DMVSettings.ValidParameterAndVariableRegexpAsRegExp.IsMatch(paramName.Value.Substring(1)))
                {
                    {
                        DataRuleProblem problem = new DataRuleProblem(this,
                                                    String.Format(CultureInfo.CurrentCulture, this.RuleProperties.Description, SqlRuleUtils.GetElementName(sqlSchemaModel, sqlElement)),
                                                    sqlElement);

                        SqlRuleUtils.UpdateProblemPosition(problem, paramName.StartOffset, paramName.FragmentLength);
                        problems.Add(problem);
                    }
                }
            }
            return problems;
        }
        #endregion
    }
}